// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/SaveGame.h"
#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
#include "Serialization/BufferArchive.h"
#include "BaseSaveGame.generated.h"

DECLARE_LOG_CATEGORY_EXTERN(LogSaveGame, Log, All)

struct FWCSaveGameArchive : public FObjectAndNameAsStringProxyArchive
{
	FWCSaveGameArchive(FArchive& InInnerArchive) : FObjectAndNameAsStringProxyArchive(InInnerArchive, true)
	{
		ArIsSaveGame = true;
		// Optional, useful when saving/loading variables without resetting the level.
		ArNoDelta = true;
		// Serialize variables even if weren't modified and mantain their default values.
	}
};

USTRUCT(BlueprintType)
struct FObjectRecord
{
	GENERATED_USTRUCT_BODY()
public:

	// class that this object is
	UPROPERTY(BlueprintReadWrite)
	UClass* Class;

	// save the outer used for object so they get loaded back in the correct hierachy
	UPROPERTY(BlueprintReadWrite)
	UObject* Outer;

	// save the outer used for object so they get loaded back in the correct hierachy
	UPROPERTY(BlueprintReadWrite)
	int32 OuterID;

	// if the outer is an actor otherwise will be UObject
	UPROPERTY(BlueprintReadWrite)
	bool bActor;

	// this is for loading only, store a pointer for the loaded object here so you can loop for the records later to de-serialize all the data
	UPROPERTY(BlueprintReadWrite)
	UObject* Self;

	// Name of the object
	UPROPERTY(BlueprintReadWrite)
	FName Name;

	// serialized data for all UProperties that are 'SaveGame' enabled
	UPROPERTY(BlueprintReadWrite)
	TArray<uint8> Data;

	// Spawn location if it's an actor
	UPROPERTY(BlueprintReadWrite)
	FTransform Transform;

	FObjectRecord()
	{
		bActor = false;
		Class = nullptr;
		Outer = nullptr;
		Self = nullptr;
	}
};

UCLASS(Blueprintable)
class MISCELLANEOUSPROJECT_API UBaseSaveGame : public USaveGame
{
	GENERATED_BODY()

public:
	// All object data in one array
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<FObjectRecord> ObjectRecords;

	// used for temp loading objects before serializing but after loading
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<UObject*> TempObjects;

	// outers that are part of the map or otherwise preloaded so won't be in the list of TempObjects
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<UObject*> PersistentOuters;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int32 NumGemsCollected;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FVector PlayerLocation;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<FRotator> ArrayOfRotationsOfTheStars;

public:
	// basically just a wrapper so you don't have to do a for loop in blueprints
	UFUNCTION(BlueprintCallable)
	void ActorArraySaver(UPARAM(ref) TArray<AActor*>& SaveActors);

	// Save individual Actors
	UFUNCTION(BlueprintCallable)
	void ActorSaver(AActor* SaveActor);

	// Create all saved actors without any data serialized yet
	UFUNCTION(BlueprintCallable)
	void ActorPreloader(AActor* WorldActor, FObjectRecord& ActorRecord);

	// basically just a wrapper so you don't have to do a for loop in blueprints
	UFUNCTION(BlueprintCallable)
	void UObjectArraySaver(UPARAM(ref) TArray<UObject*>& SaveObjects);

	// Save individual objects
	UFUNCTION(BlueprintCallable)
	void UObjectSaver(UObject* SaveObject);

	// create all saved objects without any data serialized yet
	UFUNCTION(BlueprintCallable)
	void UObjectsPreloader(AActor* WorldActor);

	// load all data after all objects exist so all pointers will load
	UFUNCTION(BlueprintCallable)
	void UObjectDataLoader();

	// serialize the data
	UFUNCTION(BlueprintCallable)
	void SaveData(UObject* Object, TArray<uint8>& Data);

	// de-serialize the data
	UFUNCTION(BlueprintCallable)
	void LoadData(UObject* Object, UPARAM(ref) TArray<uint8>& Data);

	UFUNCTION(BlueprintCallable)
	bool SaveGameDataToFile(const FString& FullFilePath);

	UFUNCTION(BlueprintCallable)
	bool LoadGameDataFromFile(const FString& FullFilePath, int32& SaveDataInt32, FVector& SaveDataVector, TArray<FRotator>& SaveDataRotatorArray);

	UFUNCTION(BlueprintCallable)
	bool SaveGameDataToFileCompressed(const FString& FullFilePath, int32& SaveDataInt32, FVector& SaveDataVector, TArray<FRotator>& SaveDataRotatorArray);

	UFUNCTION(BlueprintCallable)
	bool LoadGameDataFromFileCompressed(const FString& FullFilePath, int32& SaveDataInt32, FVector& SaveDataVector, TArray<FRotator>& SaveDataRotatorArray);

	UFUNCTION(BlueprintCallable)
	bool DoesSaveGameExist(const FString& FullFilePath);

	UFUNCTION(BlueprintCallable)
	bool SaveDataToPath(const TArray<uint8>& Data, const FString& FilePath);

protected:
	void SaveLoadData(FArchive& Ar, int32& SaveDataInt32, FVector& SaveDataVector, TArray<FRotator>& SaveDataRotatorArray);
};
